// VerifyCPP.h

#pragma once

#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
#include <wincrypt.h>
#include <vcclr.h>
#include "SafeExceptions.h"
#using <system.dll>
#using <system.xml.dll>
#using <system.security.dll>

using namespace System;
using namespace System::Xml;
using namespace System::Security::Cryptography;
using namespace System::Security::Cryptography::X509Certificates;



namespace Safe {


	/// <summary>
	/// Enumeration of possible certificate validation results.
	/// </summary>
	public enum class PathResult : int { 
		Good = 0x0000,
		CertInvalid = 0x01,
		CertRevoked = 0x02,
		CertUntrusted = 0x03,
		CertFailedPolicyCheck = 0x04,
		RevocationUnavailable = 0x05,
		ChainNotTimeValid = 0x06,
		OcspCertRevoked = 0x07,
		OcspCertInvalid = 0x08,
		OcspRevocationUnavailable = 0x09
	};


	/// <summary>
	/// Collection of static methods for validating certificates within the SAFE 
	/// signature plug-in
	/// </summary>
	public ref class CertificateValidation
	{
	private:

		/// <summary>
		/// Checks a certificate chain for time-validity.  This checks all certificates
		/// except the root certificate for validity at the time passed into the function.
		/// CertGetCertificateChain will return time-invalidity errors for a certificate
		/// regardless of what flags are passed in when the end certificate is no longer
		/// time valid at the current time.  Since this application uses secure timestamps,
		/// this behavior may be relaxed.
		/// </summary>
		/// <param name='pChain'>The certificate chain to check for time-validity</param>
		/// <param name='validationTime'>The time for which the validity check is performed</param>
		/// <return>true if the certificate chain was time-valid at the given time</return>
		static bool CheckChainTimeValidity(PCERT_SIMPLE_CHAIN pChain,FILETIME validationTime);
		

		/// <summary>
		/// Checks a PCERT_SIMPLE_CHAIN for validity.  It is assumed that the chain
		/// pased into this function has already passed time-validity and signature checks, 
		/// and all that needs to be done is check the OCSP revocation information.
		/// The following steps are performed:
		/// For each certificate in the chain (except the root certificate):
		///	1.  An OCSP response appropriate for the certificate is located in the Colletion
		///	2.  The OCSP response is parsed, and the signature on the OCSP response is checked
		///	3.  The OCSP response is checked for time-validity based on the passed-in timestamp
		///	4.  The OCSP responder certificate is signature-verified and the time-validity is checked
		///	5.  The certificate status is verified to be OK in the OCSP response
		/// If no valid, appropriate OCSP response is found for any certificate in the chain, then
		/// the RevocationUnknown flag is returned.
		/// </summary>
		/// <param name='chain'>The PCERT_SIMPLE_CHAIN to check for validity</param>
		/// <param name='base64OcspResponses'>The OCSP responses that were embedded in the document.
		/// Certificate validation will use ONLY these OCSP responses to check the revocation 
		/// status of the chain.  
		/// <param name='clockSkew'>The maximum clock skew, in milliseconds, that is allowed between
		/// the validity period of the OCSP response and the timestamp value.  This allows for a 
		/// slight time difference between the OCSP responder's clock and the timestamp server's clock.
		/// </param>
		/// <param name='validationTime'>The time for which the certificate is validated.  If this
		/// time is not extracted from a valid RFC3161 timestamp, the current time should be 
		/// passed</param>
		/// <return>The result of the path building operation</return>
		static PathResult TryChain( 
			PCERT_SIMPLE_CHAIN chain, 
			System::Collections::Generic::List<System::String^>^ base64OcspResponses, 
			long clockSkew,
			FILETIME validationTime );


		/// <summary>
		/// Validates an OCSP responder certificate.  First, the OCSP certificate is checked
		/// for the NO-CHECK extension.  If this extension is present, a path is built and
		/// validated with revocation checking disabled.  If not, then revocation checking is
		/// performed during the path construction.  If the path is successfully constructed,
		/// time validity is checked using the CheckChainTimeValidity function
		/// </summary>
		/// <param name='pOcspCert'>The OCSP certificate context</param>
		/// <param name='timestamp'>The timestamp to use for time-validity checking</param>
		/// <return>The result of the OCSP certificate path building operation</return>
		static PathResult ValidateOcspCertificate( PCCERT_CONTEXT pOcspCert, FILETIME timestamp );
		
		/// <summary>
		/// Retrieves the last error (retrieved by calling GetLastError) as a string
		/// </summary>
		/// <return>String representation of the last Windows API error</return>
		static System::String^ GetLastErrorAsString();

	public:
		/// <summary>
		/// Gets the OCSP responses for a certificate chain built from the specified certificate
		/// to a root certificate.  This is done by calling CertGetCertificateChain, and examining
		/// the resulting chain for the revocation information.  This function can throw an
		/// OcspException if OCSP responses could not be retrieved for all certificates in the chain.
		/// This function can throw a ChainBuildException if a certificate chain could not be built
		/// to a trust root, or if the certificate does not express at least one of the policies
		/// contained in the safePolicyOids parameter.
		/// </summary>
		/// <param name='cert'>The end user certificate</param>
		/// <param name='safePolicyOids'>A list of acceptable policy OIDs to use during the chain
		/// build.  If this parameter is null or empty, policy checking is not performed</param>
		/// <param name='clockSkew'>The maximum clock skew, in milliseconds, that is allowed between
		/// the validity period of the OCSP response and the timestamp value.  This allows for a 
		/// slight time difference between the OCSP responder's clock and the timestamp server's clock.</param>
		/// <return>A collection of OCSP responses.  If the function does not succeed, an Exception
		/// is thrown, and therefore nothing is returned.</return>
		static System::Collections::Generic::List<System::String^>^	GetOcspResponses(
			X509Certificate2^ cert,
			System::Collections::Generic::List<System::String^>^ safePolicyOids,
			long clockSkew);	


		/// <summary>
		/// Validates a certificate by performing a full path build and revocation check at the given
		/// time.  The revocation checking is performed using only the OCSP responses contained in the 
		/// base64OcspResponses parameter.  This is done by calling CertGetCertificateChain, and checking
		/// each simple chain using the TryChain function.  See the documentation of the TryChain function
		/// for a description of how the chain is validated.
		/// </summary>
		/// <param name='cert'>The certificate to validate</param>
		/// <param name='base64OcspResponses'>The base 64 encoded OCSP responses used to validate
		/// the signature</param>
		/// <param name='safePolicyOids'>A list of acceptable policy OIDs to use during the chain
		/// build.  If this parameter is null or empty, policy checking is not performed</param>
		/// <param name='clockSkew'>The maximum clock skew, in milliseconds, that is allowed between
		/// the validity period of the OCSP response and the timestamp value.  This allows for a 
		/// slight time difference between the OCSP responder's clock and the timestamp server's clock.</param>
		/// <return></return>
		static Safe::PathResult ValidateCertificate(
			X509Certificate2^ cert,
			System::Collections::Generic::List<System::String^>^ base64OcspResponses,
			System::Collections::Generic::List<System::String^>^ safePolicyOids,
			System::DateTime^ timestamp,
			long clockSkew
			);

		/// <summary>
		/// Converts a path building error to a String for creation of ChainBuildException 
		/// objects
		/// </summary>
		/// <param name='err'>The error status from the path building result</param>
		/// <return>A String representation of the error code</return>
		static String^ PathBuildErrorToString( DWORD err );
	};

	
}
